package com.joolun.cloud.mall.api.interceptor;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.joolun.cloud.common.core.constant.CommonConstants;
import com.joolun.cloud.common.core.util.R;
import com.joolun.cloud.common.data.tenant.TenantContextHolder;
import com.joolun.cloud.common.security.annotation.Inside;
import com.joolun.cloud.mall.api.annotation.ApiLogin;
import com.joolun.cloud.mall.api.util.ApiUtil;
import com.joolun.cloud.mall.common.constant.MallConstants;
import com.joolun.cloud.mall.common.constant.MyReturnCode;
import com.joolun.cloud.weixin.common.entity.ThirdSession;
import com.joolun.cloud.mall.api.util.ThirdSessionHolder;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.AsyncHandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.concurrent.TimeUnit;

/**
 * ThirdSession拦截器，校验每个请求的ThirdSession
 * @author
 */
@Slf4j
@Component
@AllArgsConstructor
public class ThirdSessionInterceptor implements AsyncHandlerInterceptor {

	private final RedisTemplate redisTemplate;

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
		HandlerMethod method = (HandlerMethod) handler;
		//如果是服务间调用，直接放行
		Inside inside =  method.getMethodAnnotation(Inside.class);
		if(inside != null){
			return Boolean.TRUE;
		}
		//判断访问的control是否添加ApiLogin注解
		ApiLogin apiLogin =  method.getMethodAnnotation(ApiLogin.class);
		//获取header中的客户端类型
		String clientTypeHeader = ApiUtil.getClientType(request);
		String appIdHeader = ApiUtil.getAppId(request);
		//普通h5端、没有appId的微信H5、APP，只对@ApiLogin注解的接口进行Session校验
		if(MallConstants.CLIENT_TYPE_H5.equals(clientTypeHeader) || MallConstants.CLIENT_TYPE_H5_PC.equals(clientTypeHeader) ||
				(MallConstants.CLIENT_TYPE_H5_WX.equals(clientTypeHeader) && StrUtil.isBlank(appIdHeader)) ||
				MallConstants.CLIENT_TYPE_APP.equals(clientTypeHeader) ){
			if(apiLogin != null && apiLogin.mustLogin()){//此接口必须登录才能访问
				return this.judeSession(request, response, apiLogin);
			}else{
				String tenantId = request.getHeader(MallConstants.HEADER_TENANT_ID);
				if(StrUtil.isBlank(tenantId)){
					R r = R.failed(MyReturnCode.ERR_60004.getCode(), MyReturnCode.ERR_60004.getMsg());
					this.writerPrint(response, r);
					return Boolean.FALSE;
				}
				TenantContextHolder.setTenantId(tenantId);//设置租户ID
				String thirdSessionHeader = request.getHeader(MallConstants.HEADER_THIRDSESSION);
				if(StrUtil.isNotBlank(thirdSessionHeader)){
					//获取缓存中的ThirdSession
					String key = MallConstants.THIRD_SESSION_BEGIN + ":" + thirdSessionHeader;
					Object thirdSessionObj = redisTemplate.opsForValue().get(key);
					if(thirdSessionObj == null) {//session过期
						ThirdSessionHolder.clear();
					}else {
						String thirdSessionStr = String.valueOf(thirdSessionObj);
						ThirdSession thirdSession = JSONUtil.toBean(thirdSessionStr, ThirdSession.class);
						ThirdSessionHolder.setThirdSession(thirdSession);
						redisTemplate.expire(key, MallConstants.TIME_OUT_SESSION, TimeUnit.HOURS);//更新session过期时间
					}
				}else{
					ThirdSessionHolder.clear();
				}
				return Boolean.TRUE;
			}
		}else{
			//小程序端或带有appId的微信H5的所有接口需要登录才能访问，校验thirdSession
			return this.judeSession(request, response, apiLogin);
		}
	}

	/**
	 * 校验session
	 * @param request
	 * @param response
	 * @return
	 * @throws IOException
	 */
	private boolean judeSession(HttpServletRequest request, HttpServletResponse response, ApiLogin apiLogin) throws IOException {
		//获取header中的ThirdSession
		String thirdSessionHeader = request.getHeader(MallConstants.HEADER_THIRDSESSION);
		if(StrUtil.isNotBlank(thirdSessionHeader)){
			//获取缓存中的ThirdSession
			String key = MallConstants.THIRD_SESSION_BEGIN + ":" + thirdSessionHeader;
			Object thirdSessionObj = redisTemplate.opsForValue().get(key);
			if(thirdSessionObj == null) {//session过期
				R r = R.failed(MyReturnCode.ERR_60001.getCode(), MyReturnCode.ERR_60001.getMsg());
				this.writerPrint(response, r);
				return Boolean.FALSE;
			}else {
				String thirdSessionStr = String.valueOf(thirdSessionObj);
				ThirdSession thirdSession = JSONUtil.toBean(thirdSessionStr, ThirdSession.class);
				//判断session是否属于当前tenantId、appId
				String tenantIdHeader = request.getHeader(MallConstants.HEADER_TENANT_ID);
				if(StrUtil.isNotBlank(tenantIdHeader) && !thirdSession.getTenantId().equals(tenantIdHeader)){
					R r = R.failed(MyReturnCode.ERR_60001.getCode(), MyReturnCode.ERR_60001.getMsg());
					this.writerPrint(response, r);
					return Boolean.FALSE;
				}
				String appIdHeader = ApiUtil.getAppId(request);
				if(StrUtil.isNotBlank(appIdHeader) && StrUtil.isNotBlank(thirdSession.getAppId()) && !thirdSession.getAppId().equals(appIdHeader)){
					R r = R.failed(MyReturnCode.ERR_60001.getCode(), MyReturnCode.ERR_60001.getMsg());
					this.writerPrint(response, r);
					return Boolean.FALSE;
				}
				redisTemplate.expire(key, MallConstants.TIME_OUT_SESSION, TimeUnit.HOURS);//更新session过期时间
				TenantContextHolder.setTenantId(thirdSession.getTenantId());//设置租户ID
				ThirdSessionHolder.setThirdSession(thirdSession);//设置thirdSession

				if(apiLogin != null && apiLogin.mustLogin()){
					//此接口必须登录商城才能访问
					return this.judeSessionUserMall(response, thirdSession);
				}else{
					return Boolean.TRUE;
				}
			}
		}else{
			R r = R.failed(MyReturnCode.ERR_60002.getCode(), MyReturnCode.ERR_60002.getMsg());
			this.writerPrint(response, r);
			return Boolean.FALSE;
		}
	}

	/**
	 * 校验session是否商城登录
	 * @param thirdSession
	 * @return
	 * @throws IOException
	 */
	private boolean judeSessionUserMall(HttpServletResponse response, ThirdSession thirdSession) throws IOException {
		String userId = thirdSession.getUserId();
		if(StrUtil.isBlank(userId)){
			R r = R.failed(MyReturnCode.ERR_60003.getCode(), MyReturnCode.ERR_60003.getMsg());
			this.writerPrint(response, r);
			return Boolean.FALSE;
		}
		return Boolean.TRUE;
	}

	private void writerPrint(HttpServletResponse response, R r) throws IOException {
		//返回超时错误码，触发小程序重新登录
		response.setCharacterEncoding(CommonConstants.UTF8);
		response.setContentType(MediaType.APPLICATION_JSON_VALUE);
		PrintWriter writer = response.getWriter();
		writer.print(JSONUtil.parseObj(r));
		if(writer != null){
			writer.close();
		}
	}
}
